package com.alibaba.chaosblade.box.auth;

import com.alibaba.chaosblade.box.common.common.constant.TokenConstants;
import com.alibaba.chaosblade.box.common.common.domain.user.ChaosUser;
import com.alibaba.chaosblade.box.common.common.domain.user.LoginUser;
import com.alibaba.chaosblade.box.common.common.enums.CommonErrorCode;
import com.alibaba.chaosblade.box.common.infrastructure.exception.ChaosException;
import com.alibaba.chaosblade.box.common.infrastructure.exception.PermissionDeniedException;
import com.alibaba.chaosblade.box.controller.user.SessionBaseController;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Objects;

/**
 * @author haibin
 */
@Slf4j
@Component
public class CloudLoginUserResolver implements LoginUserResolver {

    private static final List<String> WHITE_LIST = ImmutableList.of();

    @Value("${chaos.secretKey}")
    private String secretKey;

    @Override
    public ChaosUser resolve(HttpServletRequest httpServletRequest) throws ChaosException {
        String path = httpServletRequest.getRequestURI();
        if (WHITE_LIST.contains(path)) {
            return null;
        }

        HttpSession session = httpServletRequest.getSession(false);
        String header = httpServletRequest.getHeader("Authorization");
        String encodedChaos = httpServletRequest.getHeader("X-Chaos-User");
        String timestamp = httpServletRequest.getHeader("X-Chaos-Timestamp");
        String signature = httpServletRequest.getHeader("X-Chaos-Signature");

        ChaosUser chaosUser = null;

        // 优先处理 X-Chaos 认证
        if (Objects.nonNull(encodedChaos) && Objects.nonNull(timestamp) && Objects.nonNull(signature)) {
            chaosUser = handleXChaosUser(httpServletRequest, encodedChaos, timestamp, signature);
        }
        
        // 其次处理 Authorization 认证
        else if (Objects.nonNull(header) && header.startsWith(TokenConstants.PREFIX)) {
            chaosUser = handleAuthorization(header);
        }
        
        // 最后尝试从 session 获取用户信息
        else if (session != null) {
            chaosUser = handleSessionUser(session);
        }
        
        // 如果所有认证方式都失败，则抛出异常
        if (chaosUser == null) {
            throw new PermissionDeniedException(CommonErrorCode.P_LOGIN_MISSED, null);
        }

        // 如果数据库没有这个用户则直接创建一个
        SessionBaseController sessionBaseController = new SessionBaseController();
        sessionBaseController.refreshSession(chaosUser);
        return chaosUser;
    }

    /**
     * 处理 X-Chaos 用户认证
     */
    private ChaosUser handleXChaosUser(HttpServletRequest request, String encodedChaos, String timestamp, String signature) {
        // 签名校验
        boolean validSignature = Objects.equals(signature, generateSignature(encodedChaos + timestamp, secretKey));

        if (!validSignature) {
            return null;
        }

        try {
            // Step 1: Base64 解码
            byte[] decodedBytes = Base64.getDecoder().decode(encodedChaos);
            String base64DecodedStr = new String(decodedBytes, StandardCharsets.UTF_8);
            // Step 2: URL 解码
            String decodedStr = java.net.URLDecoder.decode(base64DecodedStr, StandardCharsets.UTF_8.name());
            log.info("Final decoded JSON string: {}", decodedStr);
            // Step 3: 解析为 JsonObject
            JsonObject jsonObject = new Gson().fromJson(decodedStr, JsonObject.class);

            String uid = jsonObject.get("userId").getAsString();
            String name = jsonObject.get("userName").getAsString();
            String license = jsonObject.get("license").getAsString();

            if (uid.isEmpty() || license.isEmpty()) {
                return null;
            }

            ChaosUser chaosUser = new ChaosUser();
            chaosUser.setUserId(uid);
            chaosUser.setUserName(name);
            chaosUser.setLicense(license);

            // 同步写入 session，方便后续请求使用
            HttpSession session = request.getSession(true);
            session.setAttribute("uid", uid);
            session.setAttribute("name", name);
            session.setAttribute("license", license);
            
            return chaosUser;
        } catch (JsonSyntaxException | IllegalArgumentException e) {
            log.warn("Failed to decode or parse X-Chaos-User: {}", "编码无效", e);
            throw new PermissionDeniedException(CommonErrorCode.P_INVALID_CREDENTIALS);
        } catch (Exception e) {
            log.warn("Unexpected error during decoding X-Chaos-User", e);
            throw new PermissionDeniedException(CommonErrorCode.P_GENERAL_ERROR);
        }
    }

    /**
     * 处理 Authorization 认证
     */
    private ChaosUser handleAuthorization(String header) {
        try {
            // 移除 "Bearer " 前缀
            String token = header.substring(7);
            LoginUser loginUser = AuthUtil.getLoginUser(token);
            if (loginUser == null || loginUser.getSysUser() == null) {
                return null;
            }
            
            ChaosUser chaosUser = new ChaosUser();
            chaosUser.setUserNick(loginUser.getSysUser().getNickName());
            chaosUser.setUserId(String.valueOf(loginUser.getUserid()));
            chaosUser.setUserName(loginUser.getSysUser().getUserName());
            chaosUser.setLicense(loginUser.getSysUser().getLicense());
            
            return chaosUser;
        } catch (Exception e) {
            log.warn("Failed to handle Authorization header", e);
            return null;
        }
    }

    /**
     * 处理 Session 用户认证
     */
    private ChaosUser handleSessionUser(HttpSession session) {
        try {
            String uid = (String) session.getAttribute("uid");
            String name = (String) session.getAttribute("name");
            String license = (String) session.getAttribute("license");
            
            if (uid == null || uid.isEmpty()) {
                return null;
            }
            
            ChaosUser chaosUser = new ChaosUser();
            chaosUser.setUserId(uid);
            chaosUser.setUserName(name);
            chaosUser.setLicense(license);
            
            return chaosUser;
        } catch (Exception e) {
            log.warn("Failed to handle session user", e);
            return null;
        }
    }

    /**
     * 生成 HMAC-SHA256 签名
     *
     * @param data      待签名的数据
     * @param secretKey 用于签名的密钥
     * @return 签名结果的十六进制字符串表示
     * @throws RuntimeException 当签名生成失败时抛出
     */
    public static String generateSignature(String data, String secretKey) {
        try {
            // 创建 HMAC-SHA256 密钥
            SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");

            // 获取 Mac 实例并初始化
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(keySpec);

            // 执行签名
            byte[] signatureBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));

            // 转换为十六进制字符串输出（对应 CryptoJS.enc.Hex）
            StringBuilder sb = new StringBuilder();
            for (byte b : signatureBytes) {
                sb.append(String.format("%02x", b));
            }

            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate HMAC-SHA256 signature", e);
        }
    }
}